﻿using System.Collections.Generic;
using System.Linq;
using S7PlcDataCollector.Models;

namespace S7PlcDataCollector.Service.PlcLink
{
    internal class PlcLinkManager : IPlcLinkObserver, IPlcDataObserver
    {
        private readonly IPlcLinkObserver _plcLinkObserver;
        private readonly IPlcDataObserver _plcDataObserver;
        private readonly List<PlcLinkEntity> _plcLinkEntities;

        public PlcLinkManager(IPlcLinkObserver plcLinkObserver, IPlcDataObserver plcDataObserver)
        {
            _plcLinkObserver = plcLinkObserver;
            _plcDataObserver = plcDataObserver;
            _plcLinkEntities = new List<PlcLinkEntity>();
        }

        public void Init()
        {
            var plcEntityList = S7PlcDataCollectorService.Instance.GetS7PlcEntityList();
            foreach (var plcEntity in plcEntityList)
            {
                AddPlcEntity(plcEntity);
            }
        }

        public void UnInit()
        {
            lock (_plcLinkEntities)
            {
                foreach (var linkEntity in _plcLinkEntities)
                {
                    linkEntity.UnInit();
                }

                _plcLinkEntities.Clear();
            }
        }

        public void AddPlcEntity(S7PlcEntity plcEntity)
        {
            if (plcEntity == null || plcEntity.Rid <= 0)
            {
                return;
            }

            lock (_plcLinkEntities)
            {
                var linkEntity = _plcLinkEntities.FirstOrDefault(
                    t => t.PlcEntity.Rid == plcEntity.Rid);
                if (linkEntity != null)
                {
                    return;
                }

                linkEntity = new PlcLinkEntity(plcEntity, this, this);
                linkEntity.Init();
                _plcLinkEntities.Add(linkEntity);
            }
        }

        public void RemovePlcEntity(S7PlcEntity plcEntity)
        {
            if (plcEntity == null || plcEntity.Rid <= 0)
            {
                return;
            }

            PlcLinkEntity linkEntity;
            lock (_plcLinkEntities)
            {
                linkEntity = _plcLinkEntities.FirstOrDefault(
                    t => t.PlcEntity.Rid == plcEntity.Rid);
                if (linkEntity != null)
                {
                    _plcLinkEntities.Remove(linkEntity);
                }
            }

            linkEntity?.UnInit();
        }

        public PlcLinkState GetLinkState(int plcEntityRid)
        {
            var linkState = PlcLinkState.Unknown;
            if (plcEntityRid <= 0)
            {
                return linkState;
            }

            lock (_plcLinkEntities)
            {
                var linkEntity = _plcLinkEntities.FirstOrDefault(
                    t => t.PlcEntity.Rid == plcEntityRid);
                if (linkEntity != null)
                {
                    linkState = linkEntity.LinkState;
                }
            }

            return linkState;
        }

        public void GetVariable(S7PlcVariableEntity variableEntity)
        {
            PlcLinkEntity linkEntity;
            lock (_plcLinkEntities)
            {
                linkEntity = _plcLinkEntities.FirstOrDefault(
                    t => t.PlcEntity.Rid == variableEntity.S7PlcEntityRid);
            }

            linkEntity?.GetVariable(variableEntity);
        }

        public void LinkStateChange(S7PlcEntity plcEntity, PlcLinkState linkState)
        {
            _plcLinkObserver?.LinkStateChange(plcEntity, linkState);
        }

        public void ReceivePlcData(S7PlcVariableEntity variableEntity, PlcDataResult result)
        {
            _plcDataObserver?.ReceivePlcData(variableEntity, result);
        }
    }
}
